using System;
using CI.Misc;
using CI.Organism;
using NUnit.Framework;
using Rhino.Mocks;
using CI;
using CI.Interfaces;

namespace Mesopotamia.UnitTests.Organism
{
    [TestFixture]
    public class OrganismTests
    {
        private IOrganism _organism;
        private IOrganismCollection _organisms;
        MockRepository _mocks;
        const UInt32 HEART_BEAT_COUNT = 99;
        const Int64 ENERGY = 100;
        IDNA _dna;
        readonly IHostingEnvironment _hostingEnvironment = new CI.Mesopotamia.MSRBHost(new MockHostingEnvironmentPhysical(), new CI.Mesopotamia.ScenarioLocalXML());
        private readonly SimEnv _simEnv = SimEnv.Instance;

        /// <summary>Test Harness setup</summary>
        [SetUp]
        public void Setup()
        {
            // create childOrganism collection and childOrganism
            _organisms = new CI.Organism.OrganismCollection();
            _hostingEnvironment.Organisms = _organisms;
            _organism = new CI.Organism.Organism(_hostingEnvironment);
            _dna = new CI.Organism.DNA();
            _organism.NeuralSystem = new CI.Organism.SimpleNeuralSystem(_organism);

            // setup for mocking
            _mocks = new MockRepository();

            // mock fitness function
            _hostingEnvironment.FitnessFunction = new CI.Mesopotamia.CollisionDetection();
            _hostingEnvironment.EnvironmentScenario.FitnessFunction = _hostingEnvironment.FitnessFunction.GetType().ToString();

            // add the childOrganism structure
            _organism.DNA = _dna;
        }

        /// <summary>Lets Make sure the grow method is called in the heart beat</summary>
        [Test]
        public void HeartbeatGrowTrue()
        {
            // set a dna to record teh grow
            _dna = _mocks.DynamicMock<CI.Organism.DNA>();

            // set the organisms dna
            _organism.DNA = _dna;

            //  this is what we expect to be called in a normal heartbeat
            using (_mocks.Record())
            {
                Expect.Call(_dna.Grow()).Return(true);
            }

            // run a heart beat
            using (_mocks.Playback())
            {
                _organism.Heartbeat();
            }

            // verify it got called
            _mocks.VerifyAll();

        }

        /// <summary>Lets Make sure the heart beat of the childOrganism has DNA to before tring to grow it</summary>
        [Test, ExpectedException(typeof(CI.Organism.OrganismException), ExpectedMessage = "DNA is Empty(null)")]
        public void HeartbeatDNANotNullTrue()
        {
            // clear its DNA
            _organism.DNA = null;

            _organism.Heartbeat();

        }

        /// <summary>This proceedure makes sure the heart beat count increments for the childOrganism</summary>
        [Test]
        public void HeartbeatCounterIncrementTrue()
        {
            // stub the DNA
            _organism.DNA = (CI.Organism.DNA)_mocks.Stub<CI.Organism.DNA>();

            // set the counter
            _organism.HeartbeatCount = HEART_BEAT_COUNT;

            // fire a heart beat
            _organism.Heartbeat();

            // check the counter
            Assert.AreEqual(HEART_BEAT_COUNT + 1, _organism.HeartbeatCount, "Heartbeat counter is not incrementing");

        }

        // check and make sure the energy level gets decremented on heartbeat firing
        [Test]
        public void HeartbeatEnergyDecrementTrue()
        {
            // stub the DNA
            _organism.DNA = (CI.Organism.DNA)_mocks.Stub<CI.Organism.DNA>();

            // set a nominal figure
            _organism.Energy = ENERGY;

            // fire the heartbeat
            _organism.Heartbeat();

            // check that the energy has been decremented by 1
            Assert.AreEqual(ENERGY - 1, _organism.Energy, "Organism energy wasnt decremented on heartbeat");
        }

        // check and make sure once the energy level gets to zero it dies
        [Test]
        public void HeartbeatEnergyEmptyDeathTrue()
        {
            // stub the DNA
            _organism.DNA = (CI.Organism.DNA)_mocks.Stub<CI.Organism.DNA>();

            // add the childOrganism to the collection
            _organisms.AliveAdd(_organism);

            // set it to 0
            _organism.Energy = 0;

            // fire the heartbeat
            _organism.Heartbeat();

            // check that the childOrganism is no longer in the living list
            Assert.IsFalse(_organisms.Alive.Contains(_organism), "Organism should be listed as dead :alive");

            // check that the childOrganism is now listed in the dead list
            Assert.IsTrue(_organisms.Dead.Contains(_organism), "Organism should be listed as dead :dead");

        }




        /// <summary>This proceedure makes sure the heart beat count on the childOrganism behaves when it gets to the max</summary>
        [Test, ExpectedException(typeof(CI.Organism.OrganismException), ExpectedMessage = "Heartbeat Maximum Error")]
        public void HeartbeatCounterMaxTrue()
        {
            // set it to Max Value
            _organism.HeartbeatCount = UInt32.MaxValue;
        }

        /// <summary>This proceedure makes sure hosting environment is set</summary>
        [Test, ExpectedException(typeof(CI.Organism.OrganismException), ExpectedMessage = "hostingEnvironment is null")]
        public void OrganismNullHostingEnvironmentException()
        {
            // set it to Max Value
            _organism = new CI.Organism.Organism(null);
        }

        /// <summary>Test the find search on the childOrganism collection for matching GUID</summary>
        [Test]
        public void OrganismCollectionFindExistsTrue()
        {
            // clear the collection
            _organisms.Alive.Clear();

            // add the childOrganism
            _organisms.AliveAdd(_organism);

            // now try and find it
            Assert.AreEqual(_organism, _organisms.Find(_organism.Guid));
        }

        /// <summary>Test the find search on the childOrganism collection for matching GUID</summary>
        [Test]
        public void OrganismCollectionFindMissingTrue()
        {
            // clear the collection
            _organisms.Alive.Clear();
            _organisms.Dead.Clear();

            // now try and find it
            Assert.AreEqual(null, _organisms.Find(_organism.Guid));
        }

        /// <summary>
        /// This test we DONT update the organisms loation and make sure the dirty flag is still false
        /// </summary>
        [Test]
        public void OrganismLocationSetNotChangedFalse()
        {
            // clean start
            _organism.Location = _hostingEnvironment.HostingEnvironmentPhysical.SetLocation(_organism, new CI.Organism.Location(1f, 2f, 3f));
            _organism.LocationDirty = false;

            // new location but not changed
            _organism.Location = new CI.Organism.Location(_organism.Location.XAxis, _organism.Location.YAxis, _organism.Location.ZAxis);

            // check result
            Assert.IsFalse(_organism.LocationDirty, "location dirty should have been marked false");
        }

        /// <summary>
        /// This test we update the organisms loation and make sure the dirty flag is still false
        /// </summary>
        [Test]
        public void OrganismLocationSetChangedTrue()
        {
            // clean start            
            _organism.Location = _hostingEnvironment.HostingEnvironmentPhysical.SetLocation(_organism, new CI.Organism.Location(1f, 2f, 3f));
            _organism.LocationDirty = false;

            // change one of the axis
            _organism.Location = new CI.Organism.Location(_organism.Location.XAxis + 1, _organism.Location.YAxis, _organism.Location.ZAxis);

            // check result
            Assert.IsTrue(_organism.LocationDirty, "location dirty should have been marked true");
        }
        /// <summary>Test Harness tear down</summary>
        [TearDown]
        public void Teardown()
        {


        }


    }
}


